package org.jsea.meta.api.service;

import lombok.Getter;
import lombok.Setter;
import org.jsea.meta.api.bean.MetaApiCond;
import org.jsea.meta.api.bean.MetaApiConditionGroup;
import org.jsea.meta.api.bean.MetaApiDefinitionBean;
import org.jsea.meta.api.constant.OrderType;

import java.util.*;

import static org.jsea.meta.api.constant.WhereCond.*;

@Getter
@Setter
public abstract class AbstractMetaApiHandler {

    protected static final String qm = "?";
    protected final MetaApiDefinitionBean bean;
    protected List<Object> param = new LinkedList<>();//数据仅作add和遍历操作
    protected String where;

    public AbstractMetaApiHandler(MetaApiDefinitionBean bean) {
        this.bean = bean;
        init();
    }

    protected String table() {
        return bean.getTi().getSource();
    }

    public void init() {
        parseWhere();
    }

    protected void parseWhere() {
        List<MetaApiCond> where = bean.getConditions();
        if (where.isEmpty() && bean.getId() == null && bean.getApply() == null) {
            return;
        }
        if (bean.getId() != null) {
            where.add(new MetaApiCond(bean.getTi().getPrimaryKey(), eq, bean.getId()));
        }
        StringJoiner cond = new StringJoiner(" and ");
        where.forEach(v -> cond.add(analyze(v)));
        if (bean.getApply() != null) {
            cond.add(bean.getApply());
        }
        if (bean.getApplyValues() != null) {
            param.addAll(bean.getApplyValues());
        }
        this.where = "where " + cond;
    }

    private String analyze(MetaApiCond cond) {
        if (cond.isGroup()) {
            MetaApiConditionGroup group = cond.getGroup();
            StringJoiner sj = new StringJoiner(group.isOrGroup() ? " or " : " and ");
            group.getConditions().forEach(c -> sj.add(analyze(c)));
            return "(" + sj + ")";
        }

        String key = cond.getKey();
        //对数组值特殊处理，自动转为in或者not in
        if (cond.getValue() instanceof Collection) {
            if (cond.getCond() == eq || cond.getCond() == is) {
                cond.setCond(in);
            } else if (cond.getCond() == ne || cond.getCond() == is_not) {
                cond.setCond(not_in);
            }
        }
        switch (cond.getCond()) {
            case eq:
            case ne:
            case gt:
            case ge:
            case lt:
            case le:
                param.add(cond.getValue());
                return key + ' ' + cond.getCond().getKey() + ' ' + qm;
            case in:
            case not_in:
                if (cond.getValue() instanceof Collection<?> c) {
                    StringJoiner sj = new StringJoiner(", ");
                    if (c.isEmpty()) {
                        throw new IllegalArgumentException("cond [in] value is empty.");
                    }
                    for (Object o : c) {
                        sj.add(qm);
                        param.add(o);
                    }
                    return key + cond.getCond() + "(" + sj + ")";
                } else {
                    param.add(cond.getValue());
                    return key + cond.getCond() + "(" + qm + ")";
                }
            case right_like:
                param.add(cond.getValue() + "%");
                return key + cond.getCond() + qm;
            case left_like:
                param.add("%" + cond.getValue());
                return key + cond.getCond() + qm;
            case like:
                param.add("%" + cond.getValue() + "%");
                return key + cond.getCond() + qm;
            case not_like:
                param.add("%" + cond.getValue() + "%");
                return key + not_like + qm;
            case not_left_like:
                param.add("%" + cond.getValue());
                return key + not_left_like + qm;
            case not_right_like:
                param.add(cond.getValue() + "%");
                return key + not_right_like + qm;
            case is:
            case is_not:
                if (cond.getValue() == null || "null".equalsIgnoreCase(cond.getValue().toString())) {
                    return key + cond.getCond() + cond.getValue();
                }
                param.add(cond.getValue());
                return key + cond.getCond() + qm;
            case between:
            case not_between:
                List<?> c = (List<?>) cond.getValue();
                if (c.size() != 2) {
                    throw new IllegalArgumentException("cond [between] value size is not 2 : " + cond.getValue());
                }
                param.addAll(c);
                return key + cond.getCond() + "? and ?";
        }
        throw new UnsupportedOperationException("unsupported condition : " + cond.getCond().name());
    }

    protected String orderBy() {
        if (bean.getOrder() == null || bean.getOrder().isEmpty()) {
            return null;
        }

        StringJoiner ord = new StringJoiner(", ");
        Map<String, OrderType> order = bean.getOrder();
        order.forEach((k, v) -> ord.add(k + ' ' + v.name()));
        return "order by " + ord;
    }

    public abstract String sql();
}
